import operator

import jsonpath
import allure

from common.recordlog import logs


class Assertions:
    """
    接口断言模式封装，支持：
    1.字符串包含
    2.结果相等断言
    3.结果不相等断言
    4.断言接口返回值里面的任意一个值
    5.数据库断言
    """
    # 断言状态标识，0代表成功，其他代表失败
    flag = 0

    def contains_assert(self, value, response, status_code):
        """
        第一种模式，字符串包含断言，断言预期结果的字符串是否包含在接口的实际返回结果中
        :param value:断言预期结果值
        :param response:接口返回值
        :param status_code:接口返回代码
        :return:
        """
        flag = 0
        for assert_key, assert_value in value.items():
            # print(f'{assert_key},{assert_value}')
            if assert_key == 'status_code':
                if assert_value != status_code:
                    logs.error(f'断言失败，预期结果为：{assert_value}，实际结果为：{status_code}')
                    allure.attach(f'预期结果为：{assert_value}，\n实际结果为：{status_code}',
                                  '响应失败',
                                  allure.attachment_type.TEXT)
                    flag += 1
                else:
                    logs.info(f'断言成功，预期结果为：{assert_value}，实际结果为：{status_code}')
            else:
                res_list = jsonpath.jsonpath(response, f'$..{assert_key}')
                if isinstance(res_list[0], str):
                    res_list = "".join(res_list)
                if res_list:
                    if assert_value in res_list:
                        logs.info(f'断言成功，预期结果为：{assert_value}，实际结果为：{res_list}')
                    else:
                        logs.error(f'断言失败，预期结果为：{assert_value}，实际结果为：{res_list}')
                        allure.attach(f'预期结果为：{assert_value}，\n实际结果为：{res_list}',
                                      '响应失败',
                                      allure.attachment_type.TEXT)
                        flag += 1
        return flag

    def equal_assert(self, value, response, status_code):
        """
        相等断言模式
        :param value:断言预期结果，dict
        :param response:接口实际返回结果，dict
        :param status_code:接口放回代码
        :return:flag标识，0代表通过，非0不通过
        """
        flag = 0
        res_list = []
        if isinstance(value, dict) and isinstance(response, dict):
            # 处理实际结果的数据结构，保持与预期结果的数据结构一致
            for assert_key, assert_value in response.items():
                if list(value.keys())[0] != assert_key:
                    res_list.append(assert_key)
            for rl in res_list :
                del response[rl]
            # 判断预期字典与实际字典是否一致
            eq_result=operator.eq( response, value)
            if eq_result:
                logs.info(f'相等断言成功，预期结果为：{value}，实际结果为：{response}')
            else:
                logs.error(f'相等断言失败，预期结果为：{value}，实际结果为：{response}')
                allure.attach(f'预期结果为：{value}，\n实际结果为：{response}',
                              '响应失败',
                              allure.attachment_type.TEXT)
                flag +=1
        else:
            raise TypeError("相等断言失败，参数类型错误非dict")

        return flag

    def not_equal_assert(self, value, response, status_code):
        """
        不相等断言模式
        :param value:断言预期结果，dict
        :param response:接口实际返回结果，dict
        :param status_code:接口放回代码
        :return:flag标识，0代表通过，非0不通过
        """
        flag = 0
        res_list = []
        if isinstance(value, dict) and isinstance(response, dict):
            # 处理实际结果的数据结构，保持与预期结果的数据结构一致
            for assert_key, assert_value in response.items():
                if list(value.keys())[0] != assert_key:
                    res_list.append(assert_key)
            for rl in res_list:
                del response[rl]
            # 判断预期字典与实际字典是否一致
            eq_result = operator.ne(response, value)
            if eq_result:
                logs.info(f'不相等断言成功，预期结果为：{value}，实际结果为：{response}')
            else:
                logs.error(f'不相等断言失败，预期结果为：{value}，实际结果为：{response}')
                allure.attach(f'预期结果为：{value}，\n实际结果为：{response}',
                              '响应失败',
                              allure.attachment_type.TEXT)
                flag += 1
        else:
            raise TypeError("不相等断言失败，参数类型错误非dict")

        return flag
    def assert_response_any(self,value,response ,status_code):
        """
        任意值断言模式
        :param value:预期结果
        :param response: 实际返回值结果
        :param status_code: 实际返回值代码
        :return:
        """
        flag = 0
        try:
            exp_key=list(value.keys())[0]
            if exp_key in response:
                res_value=response[exp_key]
                any_result=operator.eq(res_value,list(value.values())[0])
                if any_result :
                    logs.info(f'任意断言成功，预期结果为：{value}，实际结果为：{response}')
                else:
                    flag+=1
                    logs.error(f'任意断言失败，预期结果为：{value}，实际结果为：{response}')
        except  Exception as e:
            logs.error(f'任意值断言失败，异常信息{e}')
            raise
        return flag



    def assert_result(self, expected, response, status_code):
        """
        断言模式，通过all_flag标记
        :param expected: 预期结果
        :param response: 接口实际返回结果
        :param status_code: 接口返回的状态码
        :return:
        """
        # 0代表成功，其他代表失败
        all_flag = 0
        try:
            for expect in expected:
                for key, value in expect.items():
                    if key == 'contains':
                        all_flag += self.contains_assert(value, response, status_code)
                    elif key == 'eq':
                        all_flag += self.equal_assert(value, response, status_code)
                    elif key == 'ne':
                        all_flag += self.not_equal_assert(value, response, status_code)
                    elif key== 'any':
                        all_flag += self.assert_response_any(value, response, status_code)
            assert all_flag == 0
            logs.info('测试成功')
        except Exception as e:
            logs.error(f'测试失败，异常信息{e}')
            assert all_flag == 0
